#include <linux/tty.h>
#include <stdarg.h>
#include <string.h>
#include <linux/sched.h>
#include <linux/fs.h>

extern int vsprintf(char *buf, const char *fmt, va_list args);
extern void blk_dev_init(void);
extern void chr_dev_init(void);
extern void mem_init(long start, long end);

static int sprintf(char *str, const char *fmt, ...)
{
    va_list args;
    int i;

    va_start(args, fmt);
    i = vsprintf(str, fmt, args);
    va_end(args);
    return i;
}

__attribute__((unused)) static int strlen_2(const char * s)
{
    /*
     * res 相当于 __res
     * count 相当于 %ecx
     * ch 相当于 %al
     * %di 为 s 的地址
     */
    int res = 0;
    unsigned int count = 0xffffffff;
    unsigned char ch = '\0';
    do {
        count--;
        if (*s++ == ch)
            break;
    } while (count > 0);
    res = (0 - count);
    res -= 1;
    return res;
}

extern void print_str();
static void print(const char* str, int size)
{
    __asm__ ("call _print_str\n\t" \
        ::"b" (str), "c" (size));
}

/* 0x90002开始的2个字节，存放的是扩展内存大小，单位是KB */
#define EXT_MEM_K (*(unsigned short *)0x90002)
#define CON_ROWS ((*(unsigned short *)0x9000e) & 0xff)
#define CON_COLS (((*(unsigned short *)0x9000e) & 0xff00) >> 8)

/*
 * 0x90080地址对应的是hd0硬盘参数，共16个字节
 * 0x90090地址对应的是hd1硬盘参数，共16个字节
 * 此处的struct drive_info定义了32个字节，实际上是包含了2个硬盘参数存储
 */
#define DRIVE_INFO (*(struct drive_info *)0x90080)

/*
 * 在 bootsect.S（共512字节） 中的 508 字节处 定义了 ROOT_DEV ，占用2个字节
 * 由于bootsect.S 最终是被移动到物理地址 0x90000 处开始执行，因此ROOT_DEV对应的
 * 		物理地址就是 0x90000 + 508 = 0x901FC
 * 		开启分页后，前面16M的线性地址和物理地址是一对一映射
 * 		因此 ROOT_DEV 线性地址也是 0x901FC
 * 在 bootsect.S（共512字节） 中的 506 字节处 定义了 SWAP_DEV ，占用2个字节
 * 		因此 SWAP_DEV 线性地址是 0x901FA
 * 
 * 此处直接根据地址获取存放在地址上的两个设备号
 */
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)
#define ORIG_SWAP_DEV (*(unsigned short *)0x901FA)

static long memory_end = 0;
static long buffer_memory_end = 0;
static long main_memory_start = 0;
static char term[32] = {0};

void main(void)
{
    int len;
    char buf[64] = {0};
    len = sprintf(term, "Hello %s, ver:0.%d.", "Linux", 12);
    print(term, len);

    /* 此时中断仍未开启，还记得吗，中断是在进入保护模式之前被关掉的，在setup.s中 */

    /* 内存管理初始化 */
    /*
	 * EXT_MEM_K 是以KB为单位的扩展内存大小，因此左移10表示换算成单位MB
	 * 另外需要注意的是，EXT_MEM_K不包含最开始的1M空间，因此
	 * 	这里在计算内存末尾地址的时候，加上了1M（1<<20）
	 * 	并且这里只考虑32位空间大小
	 */
	memory_end = (1<<20) + (EXT_MEM_K<<10);
	memory_end &= 0xfffff000;
	if (memory_end > 16*1024*1024)
		memory_end = 16*1024*1024; /* 超过16M，memory_end则为16 */
	/* 内存大小 > 12M，则把缓冲区末尾是4M处 */
	if (memory_end > 12*1024*1024) 
		buffer_memory_end = 4*1024*1024;
	/* 内存大小 > 6M，则缓冲区末尾是2M处 */
	else if (memory_end > 6*1024*1024)
		buffer_memory_end = 2*1024*1024;
	/* 内存大小 < 12M，则缓冲区末尾是1M处 */
	else
		buffer_memory_end = 1*1024*1024;
	/* 主内存区域就是缓冲区后开始，到内存结束 */
	main_memory_start = buffer_memory_end;
#ifdef RAMDISK
	/* 如果定义了RAM文件系统，则需要预留 RAMDISK * 1024 字节 */
	/* 因此这种情况下，主内存起始位置要往后挪 RAMDISK * 1024 字节 */
	/* 为何不保证是4K对齐 */
	// main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif
    /*
	 * 主内存管理初始化
	 *	将1-16M这部分内存进行页管理，按照4K页大小计算被管理的页数量
     *      每个页以序号对应一个状态
	 *	对于主内存之前的内存区域，直接标记为使用状态
	 *	因此内存管理主要是管理主内存区域的这部分
	 */
	mem_init(main_memory_start,memory_end);
    len = sprintf(buf, "MEM: totoal-%dM, buf-[1M,%dM), main-[%dM,%dM)",
            (EXT_MEM_K >> 10) + 1, buffer_memory_end >> 20,
            main_memory_start >> 20, memory_end >> 20);
    print(buf, len);

    /*
	 * 设置中断处理程序
	 * int 0x80 中断，在 sched_init 中初始化
	 */
	trap_init();
    len = sprintf(buf, "trap_init ok.");
    print(buf, len);

    /* 块设备初始化 */
	blk_dev_init();
    len = sprintf(buf, "blk_dev_init ok.");
    print(buf, len);

    /* 字符设备初始化 */
	chr_dev_init();
    len = sprintf(buf, "chr_dev_init ok.");
    print(buf, len);

    /* tty 设备初始化 */
	tty_init();
    len = sprintf(buf, "tty_init ok.");
    print(buf, len);

    // /* TODO：时间管理初始化 */
	// time_init();

    /* 调度初始化 */
	sched_init();
    len = sprintf(buf, "sched_init ok.");
    print(buf, len);

    /* 高速缓冲区管理初始化 */
	buffer_init(buffer_memory_end);
    len = sprintf(buf, "buffer_init ok, buffer num=%d.", NR_BUFFERS);
    print(buf, len);

	// /* TODO：硬盘初始化 */
	// hd_init();

	// /* TODO：软盘初始化 */
	// floppy_init();

    // /* TODO：初始化完了，现在开中断，系统正式运行起来 */
    // sti();

	// /* TODO：移动到用户态：实际就是在堆栈上放置待返回的信息，使用iret 模拟中断返回 */
	// move_to_user_mode();
    // /* TODO：创建用户态任务 */
	// if (!fork()) {
	// 	init();
	// }
}